import {Injectable, NgZone} from '@angular/core';
import {ElectronService} from 'ngx-electron';
import {Observable, Subscriber} from 'rxjs';
import {nanoid} from 'nanoid';
import {asyncChannels} from '../../../../app/asyncChannels';
import IpcRenderer = Electron.IpcRenderer;

@Injectable({
  providedIn: 'root'
})
export class ElectronHttpClient {
  private ipcRenderer: IpcRenderer;

  constructor(electron: ElectronService, private zone: NgZone) {
    this.ipcRenderer = electron.ipcRenderer;
  }

  public request(config): Observable<any> {
    const uid = nanoid();
    const channel = asyncChannels.httpRequest;
    const replyChannel = asyncChannels.httpRequestReply;
    return new Observable(subscriber => {
      this.listenOne(replyChannel, uid, subscriber);
      this.ipcRenderer.send(channel, uid, config);
    });
  }

  public get(url: string, config?): Observable<any> {
    const uid = nanoid();
    const channel = asyncChannels.httpGet;
    const replyChannel = asyncChannels.httpGetReply;
    return new Observable(subscriber => {
      this.listenOne(replyChannel, uid, subscriber);
      this.ipcRenderer.send(channel, uid, url, config);
    });
  }

  public delete(url: string, config?): Observable<any> {
    const uid = nanoid();
    const channel = asyncChannels.httpDelete;
    const replyChannel = asyncChannels.httpDeleteReply;
    return new Observable(subscriber => {
      this.listenOne(replyChannel, uid, subscriber);
      this.ipcRenderer.send(channel, uid, url, config);
    });
  }

  public head(url: string, config?): Observable<any> {
    const uid = nanoid();
    const channel = asyncChannels.httpHead;
    const replyChannel = asyncChannels.httpHeadReply;
    return new Observable(subscriber => {
      this.listenOne(replyChannel, uid, subscriber);
      this.ipcRenderer.send(channel, uid, url, config);
    });
  }

  public post(url: string, data?, config?): Observable<any> {
    const uid = nanoid();
    const channel = asyncChannels.httpPost;
    const replyChannel = asyncChannels.httpPostReply;
    return new Observable(subscriber => {
      this.listenOne(replyChannel, uid, subscriber);
      this.ipcRenderer.send(channel, uid, url, data, config);
    });
  }

  public put(url: string, data?, config?): Observable<any> {
    const uid = nanoid();
    const channel = asyncChannels.httpPut;
    const replyChannel = asyncChannels.httpPutReply;
    return new Observable(subscriber => {
      this.listenOne(replyChannel, uid, subscriber);
      this.ipcRenderer.send(channel, uid, url, data, config);
    });
  }

  public patch(url: string, data?, config?): Observable<any> {
    const uid = nanoid();
    const channel = asyncChannels.httpPatch;
    const replyChannel = asyncChannels.httpPatchReply;
    return new Observable(subscriber => {
      this.listenOne(replyChannel, uid, subscriber);
      this.ipcRenderer.send(channel, uid, url, data, config);
    });
  }

  private listenOne<T>(channel: string, uid: string, subscriber: Subscriber<T>) {
    let listener;
    this.ipcRenderer.on(channel, listener = async (event, rid, [resp, err]) => {
      if (rid === uid) {
        this.ipcRenderer.removeListener(channel, listener);
        this.zone.run(() => {
          err ? subscriber.error(err) : subscriber.next(resp);
          subscriber.complete();
        });
      }
    });
  }
}
